package com.nlx.notes.module.service.impl.gold;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.nlx.notes.module.bean.dto.gold.*;
import com.nlx.notes.module.bean.vo.PageResultVo;
import com.nlx.notes.module.bean.vo.Pagination;
import com.nlx.notes.module.bean.vo.flow.GoldSaleSumOverviewVO;
import com.nlx.notes.module.bean.vo.gold.*;
import com.nlx.notes.module.core.consts.GlobalConst;
import com.nlx.notes.module.core.enums.error.GoldCodeEnum;
import com.nlx.notes.module.core.util.DataConvertorUtils;
import com.nlx.notes.module.core.util.JSONUtils;
import com.nlx.notes.module.entity.GoldOverview;
import com.nlx.notes.module.entity.GoldPurchaseEntity;
import com.nlx.notes.module.entity.GoldSaleOverviewEntity;
import com.nlx.notes.module.entity.GoldSaleRecordEntity;
import com.nlx.notes.module.service.gold.*;
import com.nlx.notes.support.handler.exception.BusinessException;
import com.nlx.notes.support.helper.gold.GoldPriceCalculateHelper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindException;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class GoldManageServiceImpl implements GoldManageService {

    final IGoldPurchaseService goldPurchaseService;
    final IGoldOverviewService goldOverviewService;

    final GoldSaleRecordService goldSaleRecordService;

    final GoldSaleOverviewService goldSaleOverviewService;

    final GoldPriceCalculateHelper goldPriceCalculateHelper;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public GoldPurchaseBaseInfoVO saveGold(GoldPurchaseAddDTO goldPurchaseAddDTO) {
        GoldPurchaseEntity goldPurchaseEntity = new GoldPurchaseEntity();
        goldPurchaseEntity.setChannel(goldPurchaseAddDTO.getChannel());
        goldPurchaseEntity.setWeight(new BigDecimal(goldPurchaseAddDTO.getWeight()));
        goldPurchaseEntity.setTotalPrice(new BigDecimal(goldPurchaseAddDTO.getTotalPrice()));
        //计算单价
        goldPurchaseEntity.setUnitPrice(goldPurchaseEntity.getTotalPrice().divide(goldPurchaseEntity.getWeight(),2, RoundingMode.HALF_UP));
        goldPurchaseEntity.setPurchaseDate(goldPurchaseAddDTO.getPurchaseDate());
        goldPurchaseEntity.setRemark(goldPurchaseAddDTO.getRemarks());
        goldPurchaseEntity.setCreatedTime(LocalDateTime.now());
        goldPurchaseEntity.setUserId(goldPurchaseAddDTO.getUserId());
        if(StrUtil.isNotEmpty(goldPurchaseAddDTO.getFeeRatio())){
            goldPurchaseEntity.setHaveFee(GlobalConst.HAVE_FEE_YES);
            goldPurchaseEntity.setFeeRatio(new BigDecimal(goldPurchaseAddDTO.getFeeRatio()));
        }else{
            goldPurchaseEntity.setHaveFee(GlobalConst.HAVE_FEE_NO);
        }
        goldPurchaseEntity.setDelFlag(GlobalConst.DEL_FLAG_NORMAL);
        boolean save = goldPurchaseService.save(goldPurchaseEntity);
        if(!save){
            log.error("保存黄金信息失败，未知错误");
            throw new BusinessException(GoldCodeEnum.SAVE_GOLD_ERROR);
        }
        //保存统计数据
        this.refreshGold(goldPurchaseAddDTO.getUserId());
        return null;
    }

    @Override
    public PageResultVo<GoldPurchasePageVO> goldPage(GoldPurchasePageDTO goldPurchasePageDTO) {
        long count = goldPurchaseService.countByCondition(goldPurchasePageDTO);
        Pagination pagination = new Pagination(goldPurchasePageDTO.getPage(),count,goldPurchasePageDTO.getPageSize());
        if(0 == count){
            return PageResultVo.success(pagination, Collections.emptyList());
        }
        List<GoldPurchaseEntity> list = goldPurchaseService.pageByCondition(goldPurchasePageDTO);
        if(CollUtil.isEmpty(list)){
            return PageResultVo.success(pagination,Collections.emptyList());
        }
        List<GoldPurchasePageVO> goldPurchasePageVOS = DataConvertorUtils.copyListProperties(list, GoldPurchasePageVO::new);
        //计算收益率
        BigDecimal currentGoldPrice = GoldPriceCalculateHelper.getCurrentGoldPrice();
        if(CollUtil.isNotEmpty(goldPurchasePageVOS)){
            for (GoldPurchasePageVO goldPurchasePageVO : goldPurchasePageVOS) {
                //判断是否存在手续费
                BigDecimal fee = new BigDecimal("0");
                BigDecimal currentValue = goldPurchasePageVO.getWeight().multiply(currentGoldPrice).setScale(2, RoundingMode.HALF_UP);
                goldPurchasePageVO.setEarningsTotal(currentValue.subtract(goldPurchasePageVO.getTotalPrice()));
                if(GlobalConst.HAVE_FEE_YES.equalsIgnoreCase(goldPurchasePageVO.getHaveFee())){
                    fee = currentValue.multiply(goldPurchasePageVO.getFeeRatio().divide(new BigDecimal("100")));
                }
                currentValue = currentValue.subtract(fee.setScale(2, RoundingMode.HALF_UP));
                goldPurchasePageVO.setEarnings(currentValue.subtract(goldPurchasePageVO.getTotalPrice()).setScale(2, RoundingMode.HALF_UP));
                goldPurchasePageVO.setFee(fee);
            }
        }
        return PageResultVo.success(pagination,goldPurchasePageVOS);
    }


    @Override
    public GoldOverviewVO overview(Long userId) {
        GoldOverview goldOverview = goldOverviewService.findNotCreateByUserId(userId);
        if(null == goldOverview){
            log.error("current user not have gold overview, userId -> {}",userId);
            throw new BusinessException(GoldCodeEnum.SAVE_GOLD_ERROR);
        }
        GoldOverviewVO goldOverviewVO = DataConvertorUtils.copyProperties(goldOverview, GoldOverviewVO::new);
        BigDecimal currentGoldPrice = GoldPriceCalculateHelper.getCurrentGoldPrice();
        if(goldOverviewVO.getWeight().compareTo(BigDecimal.ZERO)  ==  0){
            goldOverviewVO.setCurrentGoldPrice(currentGoldPrice);
            return goldOverviewVO;
        }
        if(null == currentGoldPrice){
            goldOverviewVO.setPositionProfitLoss(new BigDecimal("-1"));
        }else{
            BigDecimal currentGoldStartPrice = goldPriceCalculateHelper.getCurrentGoldStartPrice();
            goldOverviewVO.setCurrentGoldPriceChange(currentGoldPrice.subtract(currentGoldStartPrice));
            goldOverviewVO.setCurrentGoldProfitRate(goldOverviewVO.getCurrentGoldPriceChange().divide(currentGoldPrice,2,RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
            BigDecimal currentValue = currentGoldPrice.multiply(goldOverviewVO.getWeight()).setScale(2, RoundingMode.HALF_UP);
            //获取手续费
            GoldPurchaseSelectListDTO goldPurchaseSelectListDTO = new GoldPurchaseSelectListDTO();
            goldPurchaseSelectListDTO.setUserId(userId);
            goldPurchaseSelectListDTO.setHavaFee(GlobalConst.HAVE_FEE_YES);
            List<GoldPurchaseEntity> goldList = goldPurchaseService.selectListByCondition(goldPurchaseSelectListDTO);
            //总手续费
            BigDecimal feeTotal = BigDecimal.ZERO;
            if(CollUtil.isNotEmpty(goldList)){
                for (GoldPurchaseEntity goldPurchaseEntity : goldList) {
                    if(GlobalConst.HAVE_FEE_YES.equalsIgnoreCase(goldPurchaseEntity.getHaveFee())){
                        feeTotal = feeTotal.add(currentGoldPrice.multiply(goldPurchaseEntity.getWeight()).multiply(goldPurchaseEntity.getFeeRatio().divide(new BigDecimal("100"))));
                    }
                }
            }
            goldOverviewVO.setPositionFee(feeTotal.setScale(2, RoundingMode.HALF_UP));
            goldOverviewVO.setPositionProfitLoss(currentValue.subtract(goldOverviewVO.getTotalPrice().add(feeTotal)).setScale(2, RoundingMode.HALF_UP));
            goldOverviewVO.setYieldRate(goldOverviewVO.getPositionProfitLoss().divide(goldOverviewVO.getTotalPrice(),4,RoundingMode.HALF_UP).multiply(new BigDecimal("100")).setScale(2, RoundingMode.HALF_UP));
        }
        goldOverviewVO.setCurrentGoldPrice(currentGoldPrice);
        log.info("current user gold overview -> {}", JSONUtils.toString(goldOverviewVO));
        return goldOverviewVO;
    }

    @Override
    public GoldPurchaseEditVo detail(Long goldId,Long userId) {
        GoldPurchaseEntity goldPurchaseEntity = goldPurchaseService.findById(goldId,userId);
        if(null == goldPurchaseEntity){
            throw new BusinessException(GoldCodeEnum.NOT_EXISTS_GOLD_PURCHASE);
        }
        GoldPurchaseEditVo goldPurchaseEditVo = new GoldPurchaseEditVo();
        goldPurchaseEditVo.setId(goldPurchaseEntity.getId());
        goldPurchaseEditVo.setWeight(goldPurchaseEntity.getWeight());
        goldPurchaseEditVo.setTotalPrice(goldPurchaseEntity.getTotalPrice());
        goldPurchaseEditVo.setChannel(goldPurchaseEntity.getChannel());
        goldPurchaseEditVo.setPurchaseDate(goldPurchaseEntity.getPurchaseDate());
        goldPurchaseEditVo.setFeeRatio(goldPurchaseEntity.getFeeRatio());
        goldPurchaseEditVo.setRemarks(goldPurchaseEntity.getRemark());
        goldPurchaseEditVo.setUserId(goldPurchaseEntity.getUserId());
        return goldPurchaseEditVo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public GoldPurchaseEditVo editGold(GoldPurchaseEditDTO goldPurchaseEditDTO) {
        GoldPurchaseEntity goldPurchase = goldPurchaseService.findById(goldPurchaseEditDTO.getId(), goldPurchaseEditDTO.getUserId());
        if(null == goldPurchase){
            throw new BusinessException(GoldCodeEnum.NOT_EXISTS_GOLD_PURCHASE);
        }
        GoldPurchaseEntity goldPurchaseEntity = new GoldPurchaseEntity();
        goldPurchaseEntity.setId(goldPurchaseEditDTO.getId());
        goldPurchaseEntity.setChannel(goldPurchaseEditDTO.getChannel());
        goldPurchaseEntity.setWeight(new BigDecimal(goldPurchaseEditDTO.getWeight()));
        goldPurchaseEntity.setTotalPrice(new BigDecimal(goldPurchaseEditDTO.getTotalPrice()));
        //计算单价
        goldPurchaseEntity.setUnitPrice(goldPurchaseEntity.getTotalPrice().divide(goldPurchaseEntity.getWeight(),2, RoundingMode.HALF_UP));
        goldPurchaseEntity.setPurchaseDate(goldPurchaseEditDTO.getPurchaseDate());
        goldPurchaseEntity.setRemark(goldPurchaseEditDTO.getRemarks());
        goldPurchaseEntity.setUserId(goldPurchaseEditDTO.getUserId());
        if(StrUtil.isNotEmpty(goldPurchaseEditDTO.getFeeRatio())){
            goldPurchaseEntity.setHaveFee(GlobalConst.HAVE_FEE_YES);
            goldPurchaseEntity.setFeeRatio(new BigDecimal(goldPurchaseEditDTO.getFeeRatio()));
        }else{
            goldPurchaseEntity.setHaveFee(GlobalConst.HAVE_FEE_NO);
        }
        goldPurchaseEntity.setDelFlag(GlobalConst.DEL_FLAG_NORMAL);
        boolean save = goldPurchaseService.updateById(goldPurchaseEntity);
        if(!save){
            log.error("保存黄金信息失败，未知错误");
            throw new BusinessException(GoldCodeEnum.EDIT_GOLD_ERROR);
        }
        //保存统计数据
        boolean refreshed = this.refreshGold(goldPurchaseEditDTO.getUserId());
        if(!refreshed){
            log.error("刷新用户的黄金统计数据失败，错误未知");
            throw new BusinessException(GoldCodeEnum.EDIT_GOLD_ERROR);
        }
        return detail(goldPurchaseEditDTO.getId(), goldPurchaseEditDTO.getUserId());
    }

    @Override
    public GoldSaleOverviewVO findGoldSaleOverview(Long userId) {
        log.info("获取用户黄金的售卖概念蓝，用户：{}",userId);
        GoldSaleOverviewEntity goldSaleOverviewEntity = goldSaleOverviewService.findIfNotExistCreateByUserId(userId);
        if(null == goldSaleOverviewEntity){
            log.error("user {} not find gold sale overview", userId);
            throw new BusinessException(GoldCodeEnum.NOT_FOUND_GOLD_SALE_OVERVIEW_ERROR);
        }
        GoldSaleOverviewVO goldSaleOverviewVO = new GoldSaleOverviewVO();
        goldSaleOverviewVO.setId(goldSaleOverviewEntity.getId());
        goldSaleOverviewVO.setWeight(goldSaleOverviewEntity.getWeight());
        goldSaleOverviewVO.setBuyTotalPrice(goldSaleOverviewEntity.getBuyTotalPrice());
        goldSaleOverviewVO.setSaleTotalPrice(goldSaleOverviewEntity.getSaleTotalPrice());
        goldSaleOverviewVO.setBuyUnitPrice(goldSaleOverviewEntity.getBuyUnitPrice());
        goldSaleOverviewVO.setSaleUnitPrice(goldSaleOverviewEntity.getSaleUnitPrice());
        goldSaleOverviewVO.setFee(goldSaleOverviewEntity.getFee());
        goldSaleOverviewVO.setProfit(goldSaleOverviewEntity.getProfit());
        goldSaleOverviewVO.setTotalProfitLoss(goldSaleOverviewEntity.getTotalProfitLoss());
        goldSaleOverviewVO.setUserId(userId);
        return goldSaleOverviewVO;
    }

    @Override
    public PageResultVo<GoldSalePageVO> goldSalePage(GoldSalePageQueryDTO goldSalePageQueryDTO) {
        Pagination pagination = new Pagination(goldSalePageQueryDTO.getPage(),0,goldSalePageQueryDTO.getPageSize());
        IPage<GoldSaleRecordEntity> page = goldSaleRecordService.pageByCondition(goldSalePageQueryDTO);
       if(page.getTotal() == 0){
           return PageResultVo.success(pagination,Collections.emptyList());
       }
       List<GoldSaleRecordEntity> list = page.getRecords();
        pagination.setTotal(page.getTotal());
        List<GoldSalePageVO> goldSalePageVOList = DataConvertorUtils.copyListProperties(list, GoldSalePageVO::new);
        return PageResultVo.success(pagination,goldSalePageVOList);
    }

    @Transactional
    @Override
    public boolean saleGold(GoldSaleAddDTO goldSaleAddDTO) {
        GoldPurchaseEntity goldPurchase = goldPurchaseService.findById(goldSaleAddDTO.getGoldId(), goldSaleAddDTO.getUserId());
        if(null == goldPurchase){
            throw new BusinessException(GoldCodeEnum.NOT_EXISTS_GOLD_PURCHASE);
        }
        //判断是否完全卖出
        BigDecimal saleWeight = new BigDecimal(goldSaleAddDTO.getWeight());
        //更新黄金的剩余重量和金额
        BigDecimal availableWeight = goldPurchase.getWeight().subtract(saleWeight);
        if(availableWeight.compareTo(BigDecimal.ZERO) < 0){
            log.error("卖出黄金超过持有重量");
            throw new BusinessException(GoldCodeEnum.SALE_GOLD_WEIGHT_EXCEED_HOLD_WEIGHT_ERROR);
        }
        goldPurchase.setWeight(availableWeight);
        BigDecimal buyTotal   = goldPurchase.getTotalPrice();
        //重新计算购买价格
        goldPurchase.setTotalPrice(availableWeight.multiply(goldPurchase.getUnitPrice()));
        goldPurchaseService.updateById(goldPurchase);
        if(availableWeight.compareTo(BigDecimal.ZERO) == 0){
            //卖出了所有的重量
            goldPurchaseService.removeGold(goldSaleAddDTO.getGoldId(),goldSaleAddDTO.getUserId());
        }else{
            buyTotal  = new BigDecimal(goldSaleAddDTO.getWeight()).multiply(goldPurchase.getUnitPrice());
        }

        //新增一条售卖记录
        GoldSaleRecordEntity goldSaleRecordEntity = new GoldSaleRecordEntity();
        goldSaleRecordEntity.setChannel(goldSaleAddDTO.getChannel());
        goldSaleRecordEntity.setWeight(new BigDecimal(goldSaleAddDTO.getWeight()));
        goldSaleRecordEntity.setBuyTotalPrice(buyTotal);
        BigDecimal saleTotalPrice = new BigDecimal(goldSaleAddDTO.getTotalPrice());
        goldSaleRecordEntity.setSaleTotalPrice(saleTotalPrice);
        goldSaleRecordEntity.setBuyUnitPrice(goldPurchase.getUnitPrice());
        goldSaleRecordEntity.setSaleUnitPrice(saleTotalPrice.divide(new BigDecimal(goldSaleAddDTO.getWeight()),2,BigDecimal.ROUND_HALF_UP));
        goldSaleRecordEntity.setEarnings(saleTotalPrice.subtract(buyTotal));
        goldSaleRecordEntity.setProfit(goldSaleRecordEntity.getEarnings().divide(buyTotal,4,BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100)).setScale(2,BigDecimal.ROUND_HALF_UP));
        goldSaleRecordEntity.setGoldPurchaseId(goldPurchase.getId());
        goldSaleRecordEntity.setSaleDate(goldSaleAddDTO.getSaleDate());
        goldSaleRecordEntity.setUserId(goldSaleAddDTO.getUserId());
        goldSaleRecordEntity.setRemark(goldSaleAddDTO.getRemarks());
        goldSaleRecordEntity.setCreatedTime(LocalDateTime.now());
        goldSaleRecordEntity.setDelFlag(GlobalConst.DEL_FLAG_NORMAL);
        goldSaleRecordService.save(goldSaleRecordEntity);
        this.refreshSaleGold(goldSaleAddDTO.getUserId());
        this.refreshGold(goldSaleAddDTO.getUserId());
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteGold(Long goldId, Long userId) {
        GoldPurchaseEntity purchase = goldPurchaseService.findById(goldId, userId);
        if(null == purchase){
            throw new BusinessException(GoldCodeEnum.NOT_EXISTS_GOLD_PURCHASE);
        }
        boolean removed = goldPurchaseService.removeGold(goldId, userId);
        log.info("删除黄金结果：{}",removed);
        if(removed){
            this.refreshGold(userId);
        }
        return removed;
    }

    /**
     * 刷新黄金卖出概览信息
     * @param userId 用户id
     */
    private void refreshSaleGold(Long userId) {
        GoldSaleSumOverviewVO goldSaleSumOverviewVO = goldSaleRecordService.sumByUserId(userId);
        if(null == goldSaleSumOverviewVO){
            goldSaleSumOverviewVO = new GoldSaleSumOverviewVO();
        }else{
            //计算收益率
            goldSaleSumOverviewVO.setProfit(goldSaleSumOverviewVO.getTotalProfitLoss().divide(goldSaleSumOverviewVO.getBuyTotalPrice(),4,BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100)).setScale(2,BigDecimal.ROUND_HALF_UP));
        }

        if(goldSaleSumOverviewVO.getWeight().compareTo(BigDecimal.ZERO) != 0){
            goldSaleSumOverviewVO.setBuyUnitPrice(goldSaleSumOverviewVO.getBuyTotalPrice().divide(goldSaleSumOverviewVO.getWeight(),2,BigDecimal.ROUND_HALF_UP));
            goldSaleSumOverviewVO.setSaleUnitPrice(goldSaleSumOverviewVO.getSaleTotalPrice().divide(goldSaleSumOverviewVO.getWeight(),2,BigDecimal.ROUND_HALF_UP));

        }
        goldSaleSumOverviewVO.setUserId(userId);
        goldSaleOverviewService.updateGoldSale(goldSaleSumOverviewVO);
    }


    /**
     * 刷新黄金的统计数据
     * @param userId 用户数据
     */
    private boolean refreshGold(Long userId) {
        BigDecimal weight = goldPurchaseService.sumWeightByUser(userId);
        BigDecimal totalPrice = goldPurchaseService.sumTotalPriceByUser(userId);
        if(null == totalPrice){
            totalPrice = BigDecimal.ZERO;
        }
        BigDecimal unitPrice;
        if(null == weight){
            weight = BigDecimal.ZERO;
            unitPrice = BigDecimal.ZERO;
        }else{
            unitPrice = totalPrice.divide(weight,2,RoundingMode.HALF_UP);
        }
        GoldOverviewEditDTO goldOverviewEditDTO = new GoldOverviewEditDTO();
        goldOverviewEditDTO.setWeight(weight);
        goldOverviewEditDTO.setTotalPrice(totalPrice);
        goldOverviewEditDTO.setUnitPrice(unitPrice);
        goldOverviewEditDTO.setUserId(userId);
        return goldOverviewService.editGold(goldOverviewEditDTO);
    }

}
